/* eslint-disable no-console */
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
import { FBXLoader } from 'three/examples/jsm/loaders/FBXLoader.js';
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';
import { debounce } from 'lodash-es';

export default class Three3dView {
  constructor(el, options) {
    this.dom = el; // 插入的dom
    // 配置参数
    this.opt = {
      sceneUrl: '', // 场景url
      modelUrl: '', // 模型url
      autoRotate: false, // 是否自动旋转
      ...options
    };
    this.scene = null; // 场景
    this.camera = null; // 摄像机
    this.renderer = null; // 渲染器
    this.controls = null; // 控制器
    this.model = null; // 模型
    this.init();
  }
  // 初始化
  init() {
    this.initScene();
    this.initCamera();
    this.initRender();
    this.initControls();
    this.render();
    // 加载场景
    if (this.opt.sceneUrl) {
      this.loadScene(this.opt.sceneUrl);
    }
    // 加载模型
    if (this.opt.modelUrl) {
      this.loadModel(this.opt.modelUrl);
    }
    // 监听dom大小变化
    const observer = new ResizeObserver(
      debounce(entries => {
        this.updateRender();
      }, 100)
    );
    observer.observe(this.dom);
  }

  // 场景初始化
  initScene() {
    this.scene = new THREE.Scene();
    // 环境光
    const ambientLight = new THREE.AmbientLight(0xffffff, 1);
    this.scene.add(ambientLight);

    // 如果没有场景url，则添加默认的光源
    if (!this.opt.sceneUrl) {
      // 顶部平行光
      const topDirLight = new THREE.DirectionalLight(0xffffff, 1);
      topDirLight.position.set(0, 1, 0).normalize();
      this.scene.add(topDirLight);
      // 底部平行光
      const bottomDirLight = new THREE.DirectionalLight(0xffffff, 0.5);
      bottomDirLight.position.set(0, -1, 0).normalize();
      this.scene.add(bottomDirLight);
      // 半球光
      const hemLight = new THREE.HemisphereLight(0xffffff, 0x080820, 1);
      this.scene.add(hemLight);
    }
  }

  // 相机初始化
  initCamera() {
    this.camera = new THREE.PerspectiveCamera(
      70, // 摄像机视锥体垂直视野角度
      this.dom.offsetWidth / this.dom.offsetHeight, // 摄像机视锥体长宽比
      0.01, // 摄像机视锥体近端面
      2000 // 摄像机视锥体远端面
    );
    this.camera.position.set(0, 0, 1.5); // 设置相机位置
  }

  // 渲染器初始化
  initRender() {
    this.renderer = new THREE.WebGLRenderer({
      antialias: true, // 抗锯齿
      alpha: true, // canvas是否包含alpha
      logarithmicDepthBuffer: true // 对数深度缓冲
    });
    this.renderer.outputEncoding = THREE.sRGBEncoding; // 输出编码
    this.renderer.setSize(this.dom.offsetWidth, this.dom.offsetHeight);
    this.renderer.setPixelRatio(window.devicePixelRatio);
    this.dom.appendChild(this.renderer.domElement);
  }

  // 渲染方法
  render() {
    requestAnimationFrame(() => {
      this.render();
    });
    this.controls.update();
    this.renderer.render(this.scene, this.camera);
  }

  // 控制器初始化
  initControls() {
    this.controls = new OrbitControls(this.camera, this.renderer.domElement);
    this.controls.autoRotateSpeed = 1.0; // 自动旋转速度
    this.controls.autoRotate = this.opt.autoRotate; // 是否自动转动
    this.controls.enableDamping = true; // 是否惯性滑动
    this.controls.dampingFactor = 0.2; // 阻尼系数
    this.controls.enableZoom = true; // 是否允许缩放
    this.controls.rotateSpeed = 0.5; // 手动旋转速度
    this.controlsRotate(this.opt.autoRotate);
    this.controls.update();
  }

  // 控制器旋转
  controlsRotate(autoRotate) {
    this.controls.autoRotate = autoRotate || false;
    if (autoRotate) {
      // 停止操作3s后继续自动转动
      let timer = null;
      this.controls.addEventListener('start', () => {
        if (timer) {
          clearTimeout(timer);
        }
        this.controls.autoRotate = false;
      });
      this.controls.addEventListener('end', () => {
        timer = setTimeout(() => {
          this.controls.autoRotate = true;
          this.controls.update();
        }, 3000);
      });
    }
  }

  // 加载场景
  loadScene(url) {
    if (/\.hdr$/i.test(url)) {
      new RGBELoader().load(url, texture => {
        texture.mapping = THREE.EquirectangularReflectionMapping;
        this.scene.environment = texture;
        this.scene.background = texture;
      });
    } else if (/\.(jpg|jpeg|png|gif|bmp)$/i.test(url)) {
      new THREE.TextureLoader().load(url, texture => {
        texture.mapping = THREE.EquirectangularReflectionMapping;
        texture.wrapS = THREE.RepeatWrapping;
        texture.wrapT = THREE.RepeatWrapping;
        this.scene.background = texture;
        this.scene.environment = texture;
      });
    }
  }

  // 加载模型
  loadModel(url) {
    if (/\.(gltf|glb)$/i.test(url)) {
      // GLTF模型加载
      new GLTFLoader().load(
        url,
        gltf => {
          if (this.model) {
            this.scene.remove(this.model);
          }
          const model = gltf.scene;
          this.adjustCamera(model);
          this.model = model;
          this.scene.add(this.model);
        },
        xhr => {
          // console.log((xhr.loaded / xhr.total * 100) + '% loaded');
        },
        error => {
          console.error('模型加载失败:', error);
        }
      );
    } else if (/\.fbx$/i.test(url)) {
      // FBX模型加载
      new FBXLoader().load(
        url,
        obj => {
          if (this.model) {
            this.scene.remove(this.model);
          }
          this.adjustCamera(obj);
          this.model = obj;
          this.scene.add(this.model);
        },
        xhr => {
          // console.log((xhr.loaded / xhr.total * 100) + '% loaded');
        },
        error => {
          console.error('模型加载失败:', error);
        }
      );
    } else if (/\.obj$/i.test(url)) {
      // OBJ模型加载
      new OBJLoader().load(
        url,
        obj => {
          if (this.model) {
            this.scene.remove(this.model);
          }
          this.adjustCamera(obj);
          this.model = obj;
          this.scene.add(this.model);
        },
        xhr => {
          // console.log((xhr.loaded / xhr.total * 100) + '% loaded');
        },
        error => {
          console.error('模型加载失败:', error);
        }
      );
    }
  }

  // 根据模型调整相机
  adjustCamera(model) {
    const box = new THREE.Box3().setFromObject(model);
    const boxSize = box.getSize(new THREE.Vector3()).length();
    const halfFovY = THREE.MathUtils.degToRad(this.camera.fov * 0.5);
    const distance = (boxSize * 0.5) / Math.tan(halfFovY);
    this.camera.position.set(0, 0, distance);
    this.camera.updateProjectionMatrix(); // 更新相机投影矩阵
  }

  // 更新渲染
  updateRender() {
    this.camera.aspect = this.dom.offsetWidth / this.dom.offsetHeight;
    this.camera.updateProjectionMatrix(); // 更新相机投影矩阵
    this.renderer.setSize(this.dom.offsetWidth, this.dom.offsetHeight); // 更新渲染器大小
  }
}
